﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.CompositeWeb;
using Locator.LcboServices.PageLoaders.Services;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using System.Globalization;
using HtmlAgilityPack;
using Locator.LcboServices.BusinessEntities;

namespace Locator.LcboServices.LcboStoresService.Services
{
    public class LcboStoresServiceAgent : ILcboStoresService
    {
        private readonly IPageRetriever storeDetailsPageRetriever;
        private readonly IPageRetriever storeListPageRetriever;
        private readonly TraceManager traceManager;
        private readonly LogWriter logWriter;
        //private readonly IGeoLocationServiceAgent geolocationService;

        /// <summary>
        /// Initializes a new instance of the LcboStoresServiceAgent class.
        /// </summary>
        public LcboStoresServiceAgent
            (
            [ServiceDependency]IPageRetrieverFactory pageRetrieverFactory
            //,
            //[ServiceDependency]IGeoLocationServiceAgent geolocationService
            )
        {
            this.storeDetailsPageRetriever = pageRetrieverFactory.StoreDetailsPageRetriever;
            this.storeListPageRetriever = pageRetrieverFactory.StoreListPageRetriever;
            this.logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
            this.traceManager = new TraceManager(this.logWriter);
            //this.geolocationService = geolocationService;
        }

        public LcboStoreInformation GetLocationDetails(int storeNumber)
        {
            LcboStoreInformation result = new LcboStoreInformation { StoreNumber = storeNumber };
            //Dictionary<string, LcboInventoryData> inventoryDictionary = new Dictionary<string, LcboInventoryData>();
            Dictionary<string, HtmlNode> colEntries = new Dictionary<string, HtmlNode>();
            HtmlDocument myPageSource;
            try
            {
                myPageSource = this.storeDetailsPageRetriever.RetrievePageHtml(storeNumber.ToString());
            }
            catch
            {
                myPageSource = this.storeDetailsPageRetriever.RetrievePageHtml(String.Empty);
            }

            // alternate method:
            // 1. get all tds
            // 2. find one that contains text
            // 3. go to parent table
            // 4. get child trs
            HtmlNode targetTable = myPageSource.DocumentNode.SelectSingleNode("//table[@id='storeDetails']");
            HtmlNodeCollection tableRowCollection = myPageSource.DocumentNode.SelectNodes("//table[@id='infoWindow']//tr");
            List<string> trimmedStrings = new List<string>();
            foreach (HtmlNode tableRow in tableRowCollection)
            {
                string noHtml = HtmlEntity.DeEntitize(tableRow.InnerText.Trim());
                // Eliminate the empty lines
                //if (!String.IsNullOrEmpty(noHtml))
                //{
                    trimmedStrings.Add(noHtml);
                //}
            }

            result.AddressLine = ExtractStreetAddress(trimmedStrings);
            result.City = ExtractCity(trimmedStrings);
            result.TelephoneNumber = ExtractTelephoneNumber(trimmedStrings);

            HtmlNode directionsLinkNode = myPageSource.DocumentNode.SelectSingleNode("//input[@id='directionsLink']");
            result.Latitude = ExtractLatitude(directionsLinkNode);
            result.Longitude = ExtractLongitude(directionsLinkNode);
            if (!result.Latitude.HasValue || result.Latitude == 0 || !result.Longitude.HasValue || result.Longitude == 0)
            {
                //var location = geolocationService.GeoLocate(new GeoLocation.BusinessEntities.Address()
                //{
                //    AddressLine = result.AddressLine,
                //    Locality = result.City,
                //    AdminDistrict = "Ontario", 
                //    CountryRegion="Canada"
                //});

                //result.Latitude = location.Latitude;
                //result.Longitude = location.Longitude;
            }

            return result;
        }

        public List<LcboStoreInformation> GetAllStores()
        {
            List<LcboStoreInformation> result = new List<LcboStoreInformation>();
            Dictionary<int, LcboStoreInformation> resultDictionary = new Dictionary<int, LcboStoreInformation>();
            List<int> storeIds = new List<int>();
            HtmlDocument myPageSource = this.storeListPageRetriever.RetrievePageHtml(String.Empty);
            var storeNodes = myPageSource.DocumentNode.SelectNodes(String.Format("//a[@id[starts-with(.,'{0}')]]", "col"));
            if (storeNodes != null)
            {
                foreach (HtmlNode link in storeNodes)
                {
                    HtmlAttribute linkId = link.Attributes["id"];
                    string idName = linkId.Value;
                    int idNumber = Convert.ToInt32(idName.Substring("col".Length, idName.Length - "col".Length - 1));
                    LcboStoreInformation currentStore;
                    if (!resultDictionary.ContainsKey(idNumber))
                    {
                        currentStore = new LcboStoreInformation();
                        resultDictionary.Add(idNumber, currentStore);
                    }
                    else
                    {
                        currentStore = resultDictionary[idNumber];
                    }

                    char idNameLastChar = idName[idName.Length - 1];
                    switch (idNameLastChar)
                    {
                        case '0':
                            currentStore.StoreNumber = Convert.ToInt32(link.InnerText);
                            storeIds.Add(Convert.ToInt32(link.InnerText));
                            break;
                        //case '1':
                        //TextInfo textInfo = CultureInfo.CurrentCulture.TextInfo;
                        //currentStore.AddressLine = textInfo.ToTitleCase((link.InnerText.ToLower(CultureInfo.CurrentCulture)));
                        //break;
                        //case '2':
                        //this.GetDetails(link.InnerText, currentStore);
                        //if (currentProduct.Id == 0)
                        //{
                        //    resultDictionary.Remove(idNumber);
                        //}
                        // break;
                        default:
                            break;
                    }
                }
            }

            foreach (int storeId in storeIds)
            {
                var store = this.GetLocationDetails(storeId);
                result.Add(store);
            }

            //Task<HtmlDocument>[] theTasks = null;
            //List<HtmlDocument> theDocs = new List<HtmlDocument>();
            //ManualResetEvent[] syncEvent = new ManualResetEvent[] { new ManualResetEvent(false) };
            //this.storeDetailsPageRetriever.RetrievePageHtmlsAsync(storeIds.ToArray(), (docTasks) =>
            //{
            //    theTasks = docTasks;
            //    Array.ForEach(docTasks, task =>
            //        {
            //            if (task.Exception == null)
            //            {
            //                theDocs.Add(task.Result);
            //            }
            //        });

            //    syncEvent[0].Set();
            //});

            //WaitHandle.WaitAll(syncEvent);


            //foreach (var storeInfoHtmlDoc in theDocs)
            //{
                
            //}

            //HtmlNodeCollection tableRowCollection = myPageSource.DocumentNode.SelectNodes("/html[1]/body[1]/table[1]/tbody[1]/tr[2]/td[1]/table[1]/tbody[1]/tr[5]/td[2]/table[1]/tbody[1]/tr[1]/td[3]/table[1]/tbody[1]/tr[1]/td[1]/table[1]/tbody[1]/tr[1]/td[1]/table[1]/tbody[1]/tr[3]/td[1]/table[1]/tbody[1]/tr[2]/td[1]/table[1]/tbody[1]/tr");
            //List<string> trimmedStrings = new List<string>();
            //foreach (HtmlNode tableRow in tableRowCollection)
            //{
            //    trimmedStrings.Add(tableRow.InnerText.Trim());
            //}
            // /html[1]/body[1]/table[1]/tbody[1]/tr[2]/td[1]/table[1]/tbody[1]/tr[5]/td[2]/table[1]/tbody[1]/tr[1]/td[3]/table[1]/tbody[1]/tr[1]/td[1]/table[1]/tbody[1]/tr[1]/td[1]/table[1]/tbody[1]/tr[3]/td[1]/table[1]/tbody[1]/tr[2]/td[1]/table[1]/tbody[1]
            return result;
        }

        private void GetDetails(string p, LcboStoreInformation currentStore)
        {
            throw new NotImplementedException();
        }

        private static string ExtractStreetAddress(List<string> strings) 
        {
            string address = strings[2];
            string[] addressElements = address.Split(new char[] { ',' });
            if (!addressElements[0].Trim().ToUpperInvariant().StartsWith("P.O. BOX", StringComparison.InvariantCultureIgnoreCase)
                && !addressElements[0].Trim().ToUpperInvariant().StartsWith("CANADA POST OFFICE BOX", StringComparison.InvariantCultureIgnoreCase))
            {
                address = addressElements[0].Trim();
            }
            else
            {
                address = addressElements[1].Trim();
            }

            if (address.EndsWith(",", StringComparison.OrdinalIgnoreCase))
            {
                address = address.Substring(0, address.Length - 1);
            }

            TextInfo textInfo = CultureInfo.CurrentCulture.TextInfo;
            address = textInfo.ToTitleCase(address.ToLower(CultureInfo.CurrentCulture));
            return address.Substring(0, address.Length);
        }

        private static decimal? ExtractLatitude(HtmlNode link)
        {
            decimal? result;
            decimal resultNonNullable;
            string linkWithGeo = link.Attributes["value"].Value;
            string[] linkWithGeoElements = linkWithGeo.Split(new char[] { '=', '&', ',' });
            if (Decimal.TryParse(linkWithGeoElements[1], out resultNonNullable))
            {
                result = resultNonNullable;
            }
            else
            {
                result = null;
            }

            return result;
        }

        private static decimal? ExtractLongitude(HtmlNode link)
        {
            decimal? result;
            decimal resultNonNullable;
            string linkWithGeo = link.Attributes["value"].Value;
            string[] linkWithGeoElements = linkWithGeo.Split(new char[] { '=', '&', ',' });
            if (Decimal.TryParse(linkWithGeoElements[2], out resultNonNullable))
            {
                result = resultNonNullable;
            }
            else
            {
                result = null;
            }

            return result;
        }

        private static string ExtractCity(List<string> strings)
        {
            string result = strings[3];
            string[] addressElements = result.Split(new char[] { ',' });
            result = addressElements[0];
            if (result.EndsWith("-CENTRAL", StringComparison.CurrentCultureIgnoreCase) || result.EndsWith("-DOWNTOWN", StringComparison.CurrentCultureIgnoreCase))
            {
                result = result.Substring(0, result.IndexOf("-"));
            }
            else
            {
                result = result.Substring(result.IndexOf("-") + 1);
            }

            TextInfo textInfo = CultureInfo.CurrentCulture.TextInfo;
            result = textInfo.ToTitleCase(result.ToLower(CultureInfo.CurrentCulture));
            return result.Substring(0, result.Length);
        }

        private static string ExtractTelephoneNumber(List<string> strings)
        {
            string result = String.Empty;
            foreach (string line in strings)
            {
                if (line.StartsWith("Telephone", true, CultureInfo.InvariantCulture))
                {
                    result = line;
                }
            }

            string[] telephoneElements = result.Split(new char[] { ':', '-' });
            result = String.Format(CultureInfo.CurrentCulture, "({0}) {1}-{2}", telephoneElements[1].Trim(), telephoneElements[2].Trim(), telephoneElements[3].Trim());
            return result;
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.logWriter != null)
                {
                    this.logWriter.Dispose();
                }
            }
        }

        ~LcboStoresServiceAgent()
        {
            this.Dispose(false);
        }
    }
}
